﻿using Newtonsoft.Json;
using SmarterCharger.Library.Infrastructure.Interfaces;
using SmarterCharger.Library.Model;
using System.IO;
using System.IO.IsolatedStorage;
using System.Security.Cryptography;
using System.Text;
using System.Threading;

namespace SmarterCharger.Library.Infrastructure
{
    internal class SettingsService : ISettingsService
    {
        private static readonly Mutex _mutex = new Mutex();
        private const string UsernameFilePath = "SettingsUsername";
        private const string PasswordFilePath = "SettingsPassword";
        private const string DefaultSmartPlugFilePath = "SettingsDefaultSmartPlug";
        private const string ChargeLimitFilePath = "SettingsChargeLimit";
        private const string IsFirstTimeUserFilePath = "IsFirstTimeUser";

        public string GetUsername()
        {
            var encryptedData = Read(UsernameFilePath);
            if (encryptedData.Length == 0)
                return null;

            return Decrypt(encryptedData);
        }

        public string GetPassword()
        {
            var encryptedData = Read(PasswordFilePath);
            if (encryptedData.Length == 0)
                return null;

            return Decrypt(encryptedData);
        }

        public SmartPlug GetDefaultSmartPlug()
        {
            var dataAsBytes = Read(DefaultSmartPlugFilePath);
            if (dataAsBytes.Length == 0)
                return null;

            var data = Encoding.UTF8.GetString(dataAsBytes, 0, dataAsBytes.Length);

            return JsonConvert.DeserializeObject<SmartPlug>(data);
        }

        public double? GetChargeLimit()
        {
            var dataAsBytes = Read(ChargeLimitFilePath);
            if (dataAsBytes.Length == 0)
                return null;

            var data = Encoding.UTF8.GetString(dataAsBytes, 0, dataAsBytes.Length);

            return double.Parse(data);
        }

        public bool? GetIsFirstTimeUser()
        {
            var dataAsBytes = Read(IsFirstTimeUserFilePath);
            if (dataAsBytes.Length == 0)
                return null;

            var data = Encoding.UTF8.GetString(dataAsBytes, 0, dataAsBytes.Length);

            return bool.Parse(data);
        }

        public void UpdateUsername(string username)
        {
            var encryptedUsername = Encrypt(username);

            Write(encryptedUsername, UsernameFilePath);
        }

        public void UpdatePassword(string password)
        {
            var encryptedPassword = Encrypt(password);

            Write(encryptedPassword, PasswordFilePath);
        }

        public void UpdateDefaultSmartPlug(SmartPlug smartPlug)
        {
            var serializedSmartPlug = JsonConvert.SerializeObject(smartPlug);
            var dataAsBytes = Encoding.UTF8.GetBytes(serializedSmartPlug);

            Write(dataAsBytes, DefaultSmartPlugFilePath);
        }

        public void UpdateChargeLimit(double chargeLimit)
        {
            var serializedChargeLimit = JsonConvert.SerializeObject(chargeLimit);
            var dataAsBytes = Encoding.UTF8.GetBytes(serializedChargeLimit);

            Write(dataAsBytes, ChargeLimitFilePath);
        }

        public void UpdateIsFirstTimeUser(bool isFirstTimeUser)
        {
            var serializedValue = JsonConvert.SerializeObject(isFirstTimeUser);
            var dataAsBytes = Encoding.UTF8.GetBytes(serializedValue);

            Write(dataAsBytes, IsFirstTimeUserFilePath);
        }

        private byte[] Encrypt(string data)
        {
            var dataAsBytes = Encoding.UTF8.GetBytes(data);
            var encryptedData = ProtectedData.Protect(dataAsBytes, null);

            return encryptedData;
        }

        private string Decrypt(byte[] encryptedData)
        {
            if (encryptedData.Length == 0)
                return null;

            var dataAsBytes = ProtectedData.Unprotect(encryptedData, null);
            var data = Encoding.UTF8.GetString(dataAsBytes, 0, dataAsBytes.Length);

            return data;
        }

        private void Write(byte[] data, string filePath)
        {
            try
            {
                _mutex.WaitOne();

                using (var usersStore = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    using (var writestream = new IsolatedStorageFileStream(filePath, FileMode.Create, FileAccess.Write, usersStore))
                    {
                        using (var writer = new StreamWriter(writestream).BaseStream)
                        {
                            writer.Write(data, 0, data.Length);
                            writer.Close();
                            writestream.Close();
                        }
                    }
                }
            }
            finally
            {
                _mutex.ReleaseMutex();
            }
        }

        private byte[] Read(string filePath)
        {
            try
            {
                _mutex.WaitOne();

                using (var usersStore = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    using (var readstream = new IsolatedStorageFileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read, usersStore))
                    {
                        using (var reader = new StreamReader(readstream).BaseStream)
                        {
                            var dataAsBytes = new byte[reader.Length];

                            reader.Read(dataAsBytes, 0, dataAsBytes.Length);
                            reader.Close();
                            readstream.Close();

                            return dataAsBytes;
                        }
                    }
                }
            }
            finally
            {
                _mutex.ReleaseMutex();
            }
        }
    }
}
